﻿using System.ComponentModel;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using CommunityToolkit.Mvvm.Messaging;
using Dark_Core_Library.ConfigModules;
using Dark_Core_Library.DatabaseModules.MongoDB;
using Dark_Core_Library.NetworkModules.HTTP;
using Dark_Core_Library.UniModules.Models;
using DetectWebApi.Models.User;
using DnsClient;
using Microsoft.IdentityModel.Tokens;
using MongoDB.Bson;
using MongoDB.Driver;
using Serilog;

namespace DetectWebApi.Services
{
    /// <summary>
    /// 用户服务
    /// <remarks>
    /// <br></br>
    /// WeakEventMessenger：UserCreated 用户创建完成时发送
    /// <br></br>
    /// WeakEventMessenger：UserDeleted 用户删除完成时发送
    /// </remarks>
    /// </summary>
    public class UserService
    {
        #region Fields

        private readonly IMongoCollection<UserDocument> UserCollection;
        private readonly IMongoCollection<UserProfileDocument> UserProfileCollection;

        #endregion

        #region JWT Auth Fields

        private readonly string Issuer;
        private readonly string SecretKeyStr;
        private readonly int ExpiresSeconds;
        private readonly string SecurityAlgorithm;
        private readonly SymmetricSecurityKey SecretKey;

        #endregion

        #region Constructor



        public UserService()
        {
            EnvironmentJsonHelper
                .Instance
                .TryGetConfig(
                    "DB:CONNECT_STR",
                    out string dbConnectStr,
                    "mongodb://localhost:27017"
                );
            EnvironmentJsonHelper.Instance.TryGetConfig("DB:USER_DB", out string dBName, "USER");
            EnvironmentJsonHelper
                .Instance
                .TryGetConfig("DB:USER_BASIC_COL", out string basicColName, "BASIC");
            EnvironmentJsonHelper
                .Instance
                .TryGetConfig("DB:USER_PROFILE_COL", out string profileColName, "PROFILE");
            EnvironmentJsonHelper.Instance.TryGetConfig("AUTH:ISSUER", out string issuer, "ISSUER");
            EnvironmentJsonHelper
                .Instance
                .TryGetConfig("AUTH:SECRET_KEY", out string secretKeyStr, "SECRET_KEY");
            EnvironmentJsonHelper
                .Instance
                .TryGetConfig("AUTH:EXPIRES_SECONDS", out int expiresSeconds, 3600);
            MongoClient client = new MongoClient(dbConnectStr);
            this.UserCollection = client
                .GetDatabase(dBName)
                .GetCollection<UserDocument>(basicColName);
            this.UserProfileCollection = client
                .GetDatabase(dBName)
                .GetCollection<UserProfileDocument>(profileColName);
            this.Issuer = issuer;
            this.SecretKeyStr = secretKeyStr;
            this.ExpiresSeconds = expiresSeconds;
            this.SecurityAlgorithm = SecurityAlgorithms.HmacSha256;
            this.SecretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKeyStr));
            RegisterWeakEvent();
            Console.WriteLine("UserService initialized.");
        }
        #endregion

        #region Private Methods

        private void RegisterWeakEvent()
        {
            WeakReferenceMessenger
                .Default
                .Register<UniOperationResult<ObjectId, string>, string>(
                    this,
                    "UserCreated",
                    async (r, msg) => await InsertProfile(msg.Sender)
                );
            WeakReferenceMessenger
                .Default
                .Register<UniOperationResult<ObjectId, string>, string>(
                    this,
                    "UserDeleted",
                    async (r, msg) => await RemoveProfile(msg.Sender)
                );

            //WeakReferenceMessenger.Default.Register<ObjectId>
        }

        #endregion

        #region Get

        /// <summary>
        /// 获取用户总数
        /// </summary>
        /// <returns></returns>
        public async Task<UniOperationResult<object, long>> GetUserCountAsync()
        {
            try
            {
                long count = await UserCollection.EstimatedDocumentCountAsync();
                return new object().ToOperationResult(count);
            }
            catch (Exception e)
            {
                Log.Error($"UserService.GetUserCountAsync Exception:{e.Message}");
                return new object().ToOperationResult((long)0);
            }
        }

        /// <summary>
        /// 获取用户列表
        /// </summary>
        /// <param name="aggregation">操作聚合</param>
        /// <returns></returns>
        public async Task<
            UniOperationResult<
                UniOperationModeAggregation<object, UserQueryMode, UniPaginationParam<string>>,
                UserDocument[]
            >
        > GetUserListAsync(
            UniOperationModeAggregation<
                object,
                UserQueryMode,
                UniPaginationParam<string>
            > aggregation
        )
        {
            try
            {
                FindOptions<UserDocument> findOptions = new FindOptions<UserDocument>()
                {
                    Skip = aggregation.Value.PageNum * aggregation.Value.PageSize,
                    Limit = aggregation.Value.PageSize,
                };
                FilterDefinition<UserDocument> filterDefinition = aggregation.Mode switch
                {
                    UserQueryMode.All => Builders<UserDocument>.Filter.Empty,
                    UserQueryMode.Id
                        => Builders<UserDocument>
                            .Filter
                            .Eq(x => x.id, ObjectId.Parse(aggregation.Value.Value)),
                    UserQueryMode.Username
                        => Builders<UserDocument>
                            .Filter
                            .Eq(x => x.UserName, aggregation.Value.Value),
                    _ => throw new Exception("Invalid query mode.")
                };
                IAsyncCursor<UserDocument> findResult = await UserCollection.FindAsync(
                    filterDefinition,
                    findOptions
                );
                UserDocument[] queryUserResult = (await findResult.ToListAsync()).ToArray();
                return aggregation.ToOperationResult(queryUserResult);
            }
            catch (Exception e)
            {
                Log.Error($" UserService.GetUserListAsync Exception:{e.Message}");
                return aggregation.ToOperationResult(
                    Array.Empty<UserDocument>(),
                    msg: e.Message,
                    isSuccess: false
                );
            }
        }

        #endregion

        #region Insert

        public async Task<UniOperationResult<SUserDocument, string>> InsertUserAsync(
            SUserDocument sUser
        )
        {
            try
            {
                UserDocument user = UserDocument.ToDocument(sUser);
                await UserCollection.InsertOneAsync(user);
                // 原本这里是插入UserProfileDocument，最新的使用事件通知了
                //await UserProfileCollection.InsertOneAsync(profile);
                var result = sUser.ToOperationResult("User created successfully.");
                // 触发一个事件，通知用户创建完成 参数为用户
                WeakReferenceMessenger
                    .Default
                    .Send(user.id.ToOperationResult("User created successfully."), "UserCreated");
                return result;
            }
            catch (Exception e)
            {
                Log.Error($"UserService.InsertUserAsync Exception:{e.Message}");
                var result = sUser.ToOperationResult(
                    $"User creation failed:{e.Message}",
                    isSuccess: false
                );
                return result;
            }
        }

        #endregion

        #region Remove


        /// <summary>
        /// 移除用户异步
        /// </summary>
        /// <param name="userId">用户ID</param>
        /// <returns></returns>
        public async Task<UniOperationResult<ObjectId, string>> RemoveUserAsync(ObjectId userId)
        {
            try
            {
                var deleteResult = await UserCollection.DeleteOneAsync(x => x.id == userId);
                var operationResult = userId.ToOperationResult(
                    $"已移除 {deleteResult.DeletedCount} 个用户"
                );
                if (deleteResult.DeletedCount == 1)
                    WeakReferenceMessenger.Default.Send(operationResult, "UserDeleted");
                return operationResult;
            }
            catch (Exception e)
            {
                Log.Error($"UserService.RemoveUserAsync Exception:{e.Message}");
                return userId.ToOperationResult($"移除用户失败:{e.Message}");
            }
        }

        #endregion

        #region Update

        /// <summary>
        /// 更新用户密码
        /// </summary>
        /// <param name="userId">用户ID</param>
        /// <param name="newPass">新的密码</param>
        /// <returns></returns>
        public async Task<UniOperationResult<ObjectId, string>> UpdateUserPassAsync(
            ObjectId userId,
            string newPass
        )
        {
            try
            {
                var updateResult = await UserCollection.UpdateOneAsync(
                    x => x.id == userId,
                    Builders<UserDocument>.Update.Set(x => x.Password, newPass)
                );
                return userId.ToOperationResult(
                    $"已更新 {updateResult.ModifiedCount} 个用户密码",
                    isSuccess: true
                );
            }
            catch (Exception e)
            {
                Log.Error($"UserService.UpdateUserPassAsync Exception:{e.Message}");
                return userId.ToOperationResult(
                    $"已更新 {0} 个用户密码",
                    $"Update password failed:{e.Message} ",
                    isSuccess: false
                );
            }
        }


        /// <summary>
        /// 刷新用户最后登录时间
        /// </summary>
        /// <param name="userId">用户ID</param>
        /// <returns></returns>
        public async Task<UniOperationResult<ObjectId, string>> UpdateUserLastLoginTime(ObjectId userId)
        {
            try
            {
                var updateResult = await UserProfileCollection.UpdateOneAsync(
                    x => x.UserId == userId,
                    Builders<UserProfileDocument>.Update.Set(x => x.LastLoginTime, DateTime.Now)
                );
                return userId.ToOperationResult(
                    $"已更新 {updateResult.ModifiedCount} 个用户最后登录时间",
                    isSuccess: true
                );
            }
            catch (Exception e)
            {
                Log.Error($"UserService.UpdateUserLastLoginTime Exception:{e.Message}");
                return userId.ToOperationResult(
                    $"已更新 {0} 个用户密码",
                    $"Update password failed:{e.Message} ",
                    isSuccess: false
                );
            }
        }

        #endregion

        #region Profile Get

        /// <summary>
        /// 获取用户信息
        /// </summary>
        /// <param name="userId">用户ID</param>
        /// <returns></returns>
        public async Task<UniOperationResult<ObjectId, UserProfileDocument>> GetProfile(
            ObjectId userId
        )
        {
            try
            {
                var profileFindResult = (
                    await UserProfileCollection.FindAsync(x => x.UserId == userId)
                ).ToList();
                if (profileFindResult.Count != 1)
                    throw new Exception("Invalid user id.");
                var profile = profileFindResult[0];
                return userId.ToOperationResult(profile);
            }
            catch (Exception e)
            {
                Log.Error($"UserService.GetProfile Exception:{e.Message}");
                return userId.ToOperationResult(
                    new UserProfileDocument(ObjectId.Empty),
                    msg: e.Message,
                    isSuccess: false
                );
            }
        }


        /// <summary>
        /// 获取用户信息
        /// </summary>
        /// <param name="aggregation">查询聚合体</param>
        /// <returns></returns>
        public async Task<
            UniOperationResult<
                UniOperationModeAggregation<object, UserQueryMode, UniPaginationParam<string>>,
                UserProfileDocument[]
            >
        > GetProfile(UniOperationModeAggregation<object, UserQueryMode, UniPaginationParam<string>> aggregation)
        {
            try
            {

                FindOptions<UserProfileDocument> findOptions = new FindOptions<UserProfileDocument>()
                {
                    Skip = aggregation.Value.PageNum * aggregation.Value.PageSize,
                    Limit = aggregation.Value.PageSize,
                };

                FilterDefinition<UserProfileDocument> filterDefinition = aggregation.Mode switch
                {
                    UserQueryMode.All => Builders<UserProfileDocument>.Filter.Empty,
                    UserQueryMode.Id
                        => Builders<UserProfileDocument>
                            .Filter
                            .Eq(x => x.UserId, ObjectId.Parse(aggregation.Value.Value)),
                    UserQueryMode.FuzzyPhone => Builders<UserProfileDocument>.Filter.Regex(x => x.Phone,new BsonRegularExpression(aggregation.Value.Value,"i")),
                    UserQueryMode.FuzzyResponsibleProject => Builders<UserProfileDocument>.Filter.Regex(x => x.ResponsibleProject, new BsonRegularExpression(aggregation.Value.Value, "i")),
                    UserQueryMode.FuzzyDepartment => Builders<UserProfileDocument>.Filter.Regex(x => x.Department, new BsonRegularExpression(aggregation.Value.Value, "i")),
                    _ => throw new Exception("Invalid query mode.")
                };

                IAsyncCursor<UserProfileDocument> findResult = await UserProfileCollection.FindAsync(
                    filterDefinition,
                    findOptions
                );
                UserProfileDocument[] queryUserResult = (await findResult.ToListAsync()).ToArray();
                return aggregation.ToOperationResult(queryUserResult);
            }
            catch (Exception e)
            {

                Log.Error($"UserService.GetProfile Exception:{e.Message}");
                return aggregation.ToOperationResult(
                    Array.Empty<UserProfileDocument>(),
                    msg: e.Message,
                    isSuccess: false
                );
            }
        }

        #endregion

        #region Profile Insert

        /// <summary>
        /// 插入用户信息
        /// </summary>
        /// <param name="userId">用户ID</param>
        /// <returns></returns>
        public async Task<UniOperationResult<ObjectId, string>> InsertProfile(ObjectId userId)
        {
            try
            {
                UserProfileDocument profile = new UserProfileDocument(userId);
                await UserProfileCollection.InsertOneAsync(profile);
                return userId.ToOperationResult("Profile created successfully.");
            }
            catch (Exception e)
            {
                Log.Error($"UserService.InsertProfile Exception:{e.Message}");
                return userId.ToOperationResult($"Profile creation failed:{e.Message}");
            }
        }

        #endregion

        #region Profile Remove

        /// <summary>
        /// 移除用户资料
        /// </summary>
        /// <param name="userId">用户ID</param>
        /// <returns></returns>
        public async Task<UniOperationResult<ObjectId, string>> RemoveProfile(ObjectId userId)
        {
            try
            {
                var deleteResult = await UserProfileCollection.DeleteOneAsync(
                    x => x.UserId == userId
                );
                return userId.ToOperationResult($"已经移除 {deleteResult.DeletedCount} 个用户资料");
            }
            catch (Exception e)
            {
                Log.Error($" UserService.RemoveProfile Exception:{e.Message}");
                return userId.ToOperationResult($"Profile removal failed:{e.Message}");
            }
        }

        #endregion

        #region Profile Update

        public async Task<UniOperationResult<ObjectId, string>> UpdateProfile(
            ObjectId userId,
            Dictionary<string, string?> modifyFields
        )
        {
            try
            {
                UpdateDefinition<UserProfileDocument> updateDefinition = Builders<UserProfileDocument>.Update.Combine();
                if (modifyFields != null && modifyFields.ContainsKey("nickName") && modifyFields["nickName"] is not null)
                    updateDefinition = updateDefinition.Set(x => x.NickName, modifyFields["nickName"]);
                if (modifyFields != null && modifyFields.ContainsKey("phone") && modifyFields["phone"] is not null)
                    updateDefinition = updateDefinition.Set(x => x.Phone, modifyFields["phone"]);
                if(modifyFields != null && modifyFields.ContainsKey("responsibleProject") && modifyFields["responsibleProject"] is not null)
                    updateDefinition = updateDefinition.Set(x => x.ResponsibleProject, modifyFields["responsibleProject"]);
                if (modifyFields != null && modifyFields.ContainsKey("department") && modifyFields["department"] is not null)
                    updateDefinition = updateDefinition.Set(x => x.Department, modifyFields["department"]);
                if (modifyFields != null && modifyFields.ContainsKey("avatarUrl") && modifyFields["avatarUrl"] is not null)
                    updateDefinition = updateDefinition.Set(x => x.AvatarUrl, modifyFields["avatarUrl"]);
                var updateResult = await UserProfileCollection.UpdateOneAsync(x => x.UserId == userId, updateDefinition);
                return userId.ToOperationResult($"已更新用户资料 {updateResult.ModifiedCount} 个");
            }
            catch (Exception e)
            {
                Log.Error($"UserService.UpdateProfile Exception:{e.Message}");
                return userId.ToOperationResult(
                    $"Profile update failed:{e.Message}",
                    isSuccess: false
                );
            }
        }

        #endregion

        #region JWT Auth

        /// <summary>
        /// 获取JWT Token
        /// </summary>
        /// <param name="sUser">简单用户</param>
        /// <returns>操作结果</returns>
        public async Task<UniOperationResult<SUserDocument, string>> GetJwtToken(
            SUserDocument sUser
        )
        {
            try
            {
                var userFindResultList = (
                    await UserCollection.FindAsync(
                        x => x.UserName == sUser.User && x.Password == sUser.Pass
                    )
                ).ToList();
                if (userFindResultList.Count != 1)
                    throw new Exception("Invalid username or password.");
                var user = userFindResultList[0];
                var claim = new[]
                {
                    new Claim(ClaimTypes.Name, sUser.User),
                    new Claim(ClaimTypes.Role, user.Level.ToString()),
                    new Claim("Id", user.id.ToString())
                };

                var credentials = new SigningCredentials(SecretKey, SecurityAlgorithm);
                var token = new JwtSecurityToken(
                    Issuer,
                    user.UserName,
                    claim,
                    DateTime.Now,
                    DateTime.Now.AddSeconds(ExpiresSeconds),
                    credentials
                );
                var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);
                await UpdateUserLastLoginTime(user.id);
                return sUser.ToOperationResult(tokenStr, "Token generated successfully.");
            }
            catch (Exception e)
            {
                Log.Error($"UserService.GetJwtToken Exception:{e.Message}");
                return sUser.ToOperationResult(
                    "ERROR",
                    "Token generation failed.",
                    isSuccess: false
                );
            }
        }

        /// <summary>
        /// 解析Token至用户
        /// </summary>
        /// <param name="originToken">原始token字符串</param>
        /// <returns> 操作结果，其中Result为用户信息</returns>
        public async Task<UniOperationResult<string, UserDocument>> ParseTokenToUser(
            string originToken
        )
        {
            try
            {
                string token = originToken.Replace("Bearer ", "");
                var tokenHandler = new JwtSecurityTokenHandler();
                var payload = tokenHandler.ReadJwtToken(token).Payload;
                var claims = payload.Claims.ToList();
                string? id = claims.Find(x => x.Type == "Id")?.Value;
                if (!ObjectId.TryParse(id, out var userId))
                    throw new Exception("Invalid token.");
                var userFindResultList = (
                    await UserCollection.FindAsync(x => x.id == userId)
                ).ToList();
                if (userFindResultList.Count != 1)
                    throw new Exception("Invalid token.");
                UserDocument userResult = userFindResultList[0];
                return originToken.ToOperationResult(userResult);
            }
            catch (Exception e)
            {
                Log.Error($"UserService.ParseTokenToUser Exception:{e.Message}");
                return originToken.ToOperationResult(
                    UserDocument.ToDocument(new SUserDocument("ERROR", "ERROR"))
                );
            }
        }

        /// <summary>
        /// 从请求中获取用户
        /// </summary>
        /// <param name="request">请求</param>
        /// <returns>该值可能为空，表示请求中没有用户信息</returns>
        public async Task<UniOperationResult<HttpRequest, UserDocument>> GetUserFromRequest(
            HttpRequest request
        )
        {
            try
            {
                string? token = request.Headers["Authorization"];
                var parseResult = await ParseTokenToUser(token ?? "");
                if (!parseResult.IsSuccess)
                    throw new Exception("Request Get User Failed.");
                return request.ToOperationResult(parseResult.Result);
            }
            catch (Exception e)
            {
                Log.Error($"UserService.GetUserFromRequest Exception:{e.Message}");
                return request.ToOperationResult(
                    UserDocument.ToDocument(new SUserDocument("ERROR", "ERROR")),
                    isSuccess: false
                );
            }
        }

        #endregion
    }
}
